const axios = require('axios');
const URL = require('url');

class Netease{

    constructor(NeteaseConfig){
        this.NeteaseConfig = NeteaseConfig;
        this.axios = new axios.create({
            baseURL: this.NeteaseConfig.NeteaseURL ?? 'http://127.0.0.1:6657',
            timeout: 2000
        });
    }

    async getCommon(url, data){
        return new Promise((resolve, reject)=>{
            if(typeof data != 'object' && data !== undefined)
                reject("Not Object.")
            this.axios.get(`${url}${URL.format({query: data})}`).then((res, err)=>{
                if(err) reject(err);
                resolve(res);
            });
        });
    }

    async postCommon(url, data){
        return new Promise((resolve, reject)=>{
            if(typeof data != 'object' && data !== undefined) 
                reject("Not Object.");
            this.axios.post(url, data)
                .then((res, err)=>{
                    if(err) reject(err);
                    resolve(res);
                });
        });
    };

    async login(){
        this.getCommon('/login/cellphone', { phone: this.NeteaseConfig.phone, password: this.NeteaseConfig.pswd})
            .then((res)=>{
                return res;
            })
            .catch((err)=>{
                console.log(err);
            });
    };
    
    // ret: {id, name, artists, album, duration, popularity, VIP}
    async getMusicDetailById(musicIdArr){
        return new Promise((resolve, reject) => {
            if (typeof musicIdArr != 'object') reject("Type_Err: Not an Array." + typeof musicIdArr);
            let str = musicIdArr.join(',');
            this.getCommon('/song/detail', { ids: str })
                .then((res) => {
                    if(!res||!res.data||!res.data.songs) reject("NO_DATA");
                    let ret = res.data.songs.map(song => {
                        return {
                            id: song.id,
                            name: song.name,
                            artists: song.ar,
                            album: song.al ? {
                                id: song.al.id,
                                name: song.al.name,
                                picUrl: song.al.picUrl,
                            } : undefined,
                            duration: song.dt,
                            popularity: song.pop,
                            VIP: this.judgeVipByFee(song.fee),
                        }
                    })
                    resolve(ret);
                })
                .catch((err) => {
                    reject(err);
                });
        })
        
    }

    async getMusicUrlById(musicIdArr, bitrate){
        bitrate = bitrate ?? 320_000;
        return new Promise((resolve, reject)=>{
            if (typeof musicIdArr != 'object') reject("Type_Err: Not an Array." + typeof musicIdArr);
            if(typeof bitrate != 'number') reject("Type_Err: Not a Number.");
            let str = musicIdArr.join(',');
            
            this.getCommon('/song/url', {id: str, br: bitrate})
                .then((res)=>{
                    if(!res||!res.data||!res.data.data) reject("NO_DATA");
                    let ret = res.data.data.map(song => {
                        return {
                            id: song.id,
                            url: song.url,
                            gain: this.dBToMultipleGain(song.gain)
                        };
                    });
                    resolve(ret);
                })
                .catch((err)=>{
                    reject(err.data);
                });
        })
    };

    // ret: {id, name, artists, duration, VIP}
    async getMusicByName(name, limit, type){
        type = type ?? 1;
        limit = limit ?? 10;
        return new Promise((resolve, reject)=>{
            if(typeof name != 'string') reject("Type_Err: Not a String.");
            if(typeof type != 'number') reject("Type_Err: Not a Number.");
            if(typeof limit != 'number')reject("Type_Err: Not a Number.");
            this.getCommon('/search', 
                {keywords: name, type, limit})
                .then((res)=>{
                    if (!res || !res.data || !res.data.result || !res.data.result.songs){
                        reject("NO_DATA");
                        return;
                    };
                    resolve(res.data.result.songs.map(song => {
                        return {
                            id: song.id, 
                            name: song.name, 
                            artists: song.artists, 
                            duration: song.duration,
                            VIP: this.judgeVipByFee(song.fee),
                        };
                    }));
                })
                .catch((err)=>{
                    reject(err);
                });
        })

        
    };

    // ret: [{id}]
    async getMusicIdByPlaylist(playlistId){
        return new Promise(async (resolve, reject)=>{
            this.getCommon('/playlist/detail', 
                {id: playlistId})
                .then((res)=>{
                    let arr = res.data.privileges.map(item => { return item.id });
                    resolve(arr);
                })
                .catch((err)=>{
                    reject(err);
                });
        })

    }

    async getMusicIdByArtistId(artistId, order, quantity){
        return new Promise((resolve, reject)=>{
            if(!artistId) reject("PARAM_ERR");
            if(order && order!=='hot' && order!=='time') reject("PARAM_ERR");
            quantity = quantity ?? 10;
            this.getCommon('/artist/songs',{
                id: artistId,
                order,
                limit: quantity
            })
                .then((res)=>{
                    let arr = res.data.songs.map( item => {
                        return {id: item.id, name: item.name, artist: item.ar[0].name};
                    });
                    resolve(arr);
                })
                .catch((err)=>{
                    reject(err);
                });
        });
    }

    judgeVipByFee(fee){
        if (fee === 0 || fee === 8) return false;
        if (fee === 2 || fee === 4) return true;
        return true;
    }

    dBToMultipleGain(dB_gain) {
        return (Math.pow(10, dB_gain / 20) * 0.3).toFixed(2);
    }
}

module.exports = Netease;
